package org.hackystat.ambientdevice.device.orb;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.hackystat.ambientdevice.ConfigurationException;
import org.hackystat.ambientdevice.device.AmbientAction;
import org.hackystat.ambientdevice.device.AmbientDevice;
import org.hackystat.ambientdevice.server.jaxb.Action;
import org.restlet.Client;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Request;
import org.restlet.data.Response;

/**
 * Class that represent an AmbientOrb. There will be only one instance for each device ID.
 * (Singleton for every ID)
 * 
 * @author Shaoxuan Zhang
 * 
 */
public class AmbientOrb extends AmbientDevice implements Serializable {
  /** A map that store AmbientOrb objects with their device ID. */
  private static final Map<String, AmbientOrb> ORB_LIST = new HashMap<String, AmbientOrb>();
  /** serialVersionUID. */
  private static final long serialVersionUID = 702434880756074596L;
  /** {@literal} SERVER the default Ambient service URL. */
  public static final String SERVER 
    = "http://www.myambient.com:8080/java/my_devices/submitdata.jsp";
  /** {@literal} QUERYMARK indicator. */
  public static final String QUERYMARK = "?";
  /** {@literal} AMPERSAND indicator. */
  public static final String AMPERSAND = "&";
  /** deviceID of the Orb. */
  private final String deviceID; // required.
  /** Color of the Orb. */
  private Color color; // required
  /** Animation of the Orb. */
  private Animation anim; // optional (defaults to "none").
  /** Comment. */
  private String comment; // optional (defaults to "no comment").

  /**
   * Enumeration class of Color for AmbientOrb.
   * 
   * @author Shaoxuan Zhang
   */
  public enum Color {
    /** 0. */
    RED, /** 1. */
    SCARLET, /** 2. */
    TANGERINE,
    /** 3. */
    ORANGE, /** 4. */
    AMBER, /** 5. */
    GOLD, /** 6. */
    YELLOW,
    /** 7. */
    TURBO, /** 8. */
    CHARTREUSE, /** 9. */
    LIME, /** 10. */
    BRIGHTGREEN,
    /** 11. */
    HARLEQUIN, /** 12. */
    GREEN, /** 13. */
    MALACHITE, /** 14. */
    SPRINGGREEN,
    /** 15. */
    BRIGHTTURQUOISE, /** 16. */
    AQUA, /** 17. */
    TURQUOISE, /** 18. */
    CERULEAN,
    /** 19. */
    AZURE, /** 20. */
    BLUERIBBON, /** 21. */
    BLUE, /** 22. */
    ELECTRICVIOLET,
    /** 23. */
    LUCKYPOINT, /** 24. */
    INDIGO, /** 25. */
    HONEYFLOWER, /** 26. */
    METEORITE,
    /** 27. */
    MAGENTA, /** 28. */
    SEANCE, /** 29. */
    DISCO, /** 30. */
    VIOLET,
    /** 31. */
    PURPLEPIZZAZZ, /** 32. */
    CERISE, /** 33. */
    ROSE, /** 34. */
    REDRIBBON,
    /** 35. */
    TORCH, /** 36. */
    WHITE, /** invalid. */
    Unsupported;
    /**
     * Return a DEVICE enumeration corresponding to the string. If no corresponding enumeration is
     * found, Unsupported will be returned.
     * 
     * @param string the string
     * @return enumeration of DEVICE
     */
    public static Color getEnumValue(String string) {
      try {
        return Color.valueOf(string);
      }
      catch (IllegalArgumentException e) {
        return Unsupported;
      }
    }
  }

  /**
   * Enumeration class of Animation for AmbientOrb.
   * 
   * @author Shaoxuan Zhang
   */
  public enum Animation {
    /** 0. */
    NONE, /** 1. */
    VERYSLOW, /** 2. */
    SLOW, /** 3. */
    MEDIUMSLOW,
    /** 4. */
    MEDIUM, /** 5. */
    MEDIUMFAST, /** 6. */
    FAST, /** 7. */
    VERYFAST,
    /** 8. */
    CRESCENDO, /** 9. */
    HEARTBEAT, /** invalid. */
    Unsupported;
    /**
     * Return a DEVICE enumeration corresponding to the string. If no corresponding enumeration is
     * found, Unsupported will be returned.
     * 
     * @param string the string
     * @return enumeration of DEVICE
     */
    public static Animation getEnumValue(String string) {
      try {
        return Animation.valueOf(string);
      }
      catch (IllegalArgumentException e) {
        return Unsupported;
      }
    }
  }

  /** Enumeration class of AmbientOrb's action. */
  enum AMBIENTORB_ACTION {
    ChangeColor, ChangeAnimation, Unsupported;
    /**
     * Return a AMBIENTORB_ACTION enumeration corresponding to the string. If no corresponding
     * enumeration is found, Unsupported will be returned.
     * 
     * @param string the string
     * @return enumeration of AMBIENTORB_ACTION
     */
    public static AMBIENTORB_ACTION getEnumValue(String string) {
      try {
        return AMBIENTORB_ACTION.valueOf(string);
      }
      catch (IllegalArgumentException e) {
        return Unsupported;
      }
    }
  };

  /**
   * Constructs an Ambient Orb with default state.
   * 
   * @param deviceID the device ID that represent the Orb.
   */
  private AmbientOrb(String deviceID) {
    super();
    this.deviceID = deviceID;
    this.color = Color.RED;
    this.anim = Animation.NONE;
    this.comment = "AmbientOrb in Hackystat.";
  }

  /**
   * Return the AmbientOrb object that with the given device ID. If the instance is not existed, it
   * will be generated.
   * 
   * @param deviceID String that represents the device ID.
   * @return a AmbientOrb object with the given ID.
   */
  public static AmbientOrb getOrb(String deviceID) {
    AmbientOrb orb = ORB_LIST.get(deviceID);
    if (orb == null) {
      orb = new AmbientOrb(deviceID);
      ORB_LIST.put(deviceID, orb);
    }
    return orb;
  }

  /**
   * Update the AmbientOrb's state by sending HTTP request to the AmbientOrb control server.
   * 
   * @return respone state from the control server.
   */
  public String updateState() {
    // Prepare the request
    Request request = new Request(Method.POST, this.getUrl());
    request.setReferrerRef("http://www.hackystat.org");

    // Handle it using an HTTP client connector
    Client client = new Client(Protocol.HTTP);
    Response response = client.handle(request);

    if (response.getStatus().isSuccess()) {
      return Boolean.TRUE.toString();
    }
    else {
      return response.getStatus().getDescription();
    }
  }

  /**
   * @return the query portion of the URL.
   */
  private String getQuery() {
    return QUERYMARK + "devID=" + this.deviceID + AMPERSAND + "anim=" + this.anim.ordinal()
        + AMPERSAND + "color=" + this.color.ordinal() + AMPERSAND + "comment=" + this.comment;
  }

  /**
   * @return the URL that sets the Orb state.
   */
  public String getUrl() {
    return SERVER + getQuery();
  }

  /**
   * @return the devID
   */
  public String getDeviceID() {
    return this.deviceID;
  }

  /**
   * @return the color
   */
  public Color getColor() {
    return color;
  }

  /**
   * @param color the color to set
   */
  public void setColor(String color) {
    this.color = Color.valueOf(color);
  }

  /**
   * @param color the color to set
   */
  public void setColor(Color color) {
    this.color = color;
  }

  /**
   * @return the anim
   */
  public Animation getAnim() {
    return anim;
  }

  /**
   * @param anim the anim to set
   */
  public void setAnim(Animation anim) {
    this.anim = anim;
  }

  /**
   * @param anim the anim to set
   */
  public void setAnim(String anim) {
    this.anim = Animation.valueOf(anim);
  }

  /**
   * @return the comment
   */
  public String getComment() {
    return comment;
  }

  /**
   * @param comment the comment to set
   */
  public void setComment(String comment) {
    this.comment = comment;
  }

  /**
   * Return an AmbientOrb action according to the Action object.
   * 
   * @param action the specified Action object.
   * @return an AmbientAction
   * @throws ConfigurationException for errors 
   */
  @Override
  public AmbientAction getAction(Action action) throws ConfigurationException {
    if (action.getValue() == null) {
      throw new ConfigurationException(action.getActionType() + ": Value cannot be null");
    }
    switch (AMBIENTORB_ACTION.getEnumValue(action.getActionType())) {
      case ChangeColor:
        return new ChangeColor(this, action.getValue());
      case ChangeAnimation:
        return new ChangeAnimation(this, action.getValue());
      default:
        throw new ConfigurationException("Action " + action.getActionType() + " is not supported"
          + " by the AmbientOrb.");
    }
  }

}
